#include "parser.h"

#include <sstream>

#define INHIBIT true
#define DISINHIBIT false

const std::string kLEX = "LEX";
const std::string kDET = "DET";
const std::string kSUBJ = "SUBJ";
const std::string kVERB = "VERB";
const std::string kOBJ = "OBJ";
const std::string kADJ = "ADJ";
const std::string kADVERB = "ADVERB";
const std::string kPREP = "PREP";
const std::string kPREP_P = "PREP_P";

const int kLEX_SIZE = 20;
const float kDEFAULT_BETA = 0.2;
const float kINTERAREA_BETA = 0.5;
const float kRECURRENT_BETA = 0.05;
const float kLEX_BETA = 1.0;

const std::vector<std::string> kAREAS = {kLEX, kDET, kSUBJ, kVERB, kOBJ, kADJ, kADVERB, kPREP, kPREP_P};
const std::vector<std::string> kEXPLICIT_AREAS = {kLEX};
const std::vector<std::string> kRECURRENT_AREAS = {kSUBJ, kVERB, kOBJ, kADJ, kADVERB, kPREP, kPREP_P};

BioParser::GenericRules genericNoun(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {

            },
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kSUBJ, DISINHIBIT, 0},
                {kLEX, kOBJ, DISINHIBIT, 0},
                {kLEX, kPREP_P, DISINHIBIT, 0},
                {kDET, kSUBJ, DISINHIBIT, 0},
                {kDET, kOBJ, DISINHIBIT, 0},
                {kDET, kPREP_P, DISINHIBIT, 0},
                {kADJ, kSUBJ, DISINHIBIT, 0},
                {kADJ, kOBJ, DISINHIBIT, 0},
                {kADJ, kPREP_P, DISINHIBIT, 0},
                {kVERB, kOBJ, DISINHIBIT, 0},
                {kPREP_P, kPREP, DISINHIBIT, 0},
                {kPREP_P, kSUBJ, DISINHIBIT, 0},
                {kPREP_P, kOBJ, DISINHIBIT, 0}}},

        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kDET, INHIBIT, 0},
                {kADJ, INHIBIT, 0},
                {kPREP, INHIBIT, 0},
                {kPREP_P, INHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kSUBJ, INHIBIT, 0},
                {kLEX, kOBJ, INHIBIT, 0},
                {kLEX, kPREP_P, INHIBIT, 0},
                {kADJ, kSUBJ, INHIBIT, 0},
                {kADJ, kOBJ, INHIBIT, 0},
                {kADJ, kPREP_P, INHIBIT, 0},
                {kDET, kSUBJ, INHIBIT, 0},
                {kDET, kOBJ, INHIBIT, 0},
                {kDET, kPREP_P, INHIBIT, 0},
                {kVERB, kOBJ, INHIBIT, 0},
                {kPREP_P, kPREP, INHIBIT, 0},
                {kPREP_P, kVERB, INHIBIT, 0},
                {kLEX, kSUBJ, DISINHIBIT, 1},
                {kLEX, kOBJ, DISINHIBIT, 1},
                {kDET, kSUBJ, DISINHIBIT, 1},
                {kDET, kOBJ, DISINHIBIT, 1},
                {kADJ, kSUBJ, DISINHIBIT, 1},
                {kADJ, kOBJ, DISINHIBIT, 1},
                {kPREP_P, kSUBJ, INHIBIT, 0},
                {kPREP_P, kOBJ, INHIBIT, 0},
                {kVERB, kADJ, INHIBIT, 0}}}};
}

BioParser::GenericRules genericTransVerb(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kADVERB, DISINHIBIT, 1}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kVERB, DISINHIBIT, 0},
                {kVERB, kSUBJ, DISINHIBIT, 0},
                {kVERB, kADVERB, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kOBJ, DISINHIBIT, 0},
                {kSUBJ, INHIBIT, 0},
                {kADVERB, INHIBIT, 0},
            },
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kVERB, INHIBIT, 0},
                {kPREP_P, kVERB, DISINHIBIT, 0}}}};
}

BioParser::GenericRules genericIntransVerb(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kADVERB, DISINHIBIT, 1}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kVERB, DISINHIBIT, 0},
                {kVERB, kSUBJ, DISINHIBIT, 0},
                {kVERB, kADVERB, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kSUBJ, INHIBIT, 0},
                {kADVERB, INHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kVERB, INHIBIT, 0},
                {kPREP_P, kVERB, DISINHIBIT, 0}}}};
}

BioParser::GenericRules genericCopula(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {

            },
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kVERB, DISINHIBIT, 0},
                {kVERB, kSUBJ, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kSUBJ, INHIBIT, 0},
                {kOBJ, DISINHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kVERB, INHIBIT, 0},
                {kADJ, kVERB, DISINHIBIT, 0}}}};
}

BioParser::GenericRules genericAdverb(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kADVERB, DISINHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kADVERB, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kADVERB, INHIBIT, 1}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kADVERB, INHIBIT, 0}}}};
}

BioParser::GenericRules genericAdjective(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kADJ, DISINHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kADJ, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {

            },
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kADJ, INHIBIT, 0},
                {kVERB, kADJ, INHIBIT, 0}}}};
}

BioParser::GenericRules genericDeterminant(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kDET, DISINHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kDET, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {

            },
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kDET, INHIBIT, 0},
                {kVERB, kADJ, INHIBIT, 0}}}};
}

BioParser::GenericRules genericPreposition(int index)
{
    using namespace BioParser;
    return {
        index,
        // AreaAndFiberRules pre_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kPREP, DISINHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kPREP, DISINHIBIT, 0}}},
        // AreaAndFiberRules post_rules
        {
            // std::vector<AreaRule> area_rules
            {
                {kPREP_P, DISINHIBIT, 0}},
            // std::vector<FiberRule> fiber_rules
            {
                {kLEX, kPREP, INHIBIT, 0},
                {kLEX, kSUBJ, INHIBIT, 1},
                {kLEX, kOBJ, INHIBIT, 1},
                {kDET, kSUBJ, INHIBIT, 1},
                {kDET, kOBJ, INHIBIT, 1},
                {kADJ, kSUBJ, INHIBIT, 1},
                {kADJ, kOBJ, INHIBIT, 1}}}};
}

const std::map<std::string, BioParser::GenericRules> kLEXEME_DICT = {
    {"the", genericDeterminant(0)},
    {"a", genericDeterminant(1)},
    {"dogs", genericNoun(2)},
    {"cats", genericNoun(3)},
    {"mice", genericNoun(4)},
    {"people", genericNoun(5)},
    {"chase", genericTransVerb(6)},
    {"love", genericTransVerb(7)},
    {"bite", genericTransVerb(8)},
    {"of", genericPreposition(9)},
    {"bad", genericAdjective(10)},
    {"big", genericAdjective(11)},
    {"run", genericIntransVerb(12)},
    {"fly", genericIntransVerb(13)},
    {"quickly", genericAdverb(14)},
    {"in", genericPreposition(15)},
    {"are", genericCopula(16)},
    {"man", genericNoun(17)},
    {"woman", genericNoun(18)},
    {"saw", genericTransVerb(19)}};

const std::map<std::string, std::vector<std::string>> kENGLISH_READOUT_RULES = {
    {kVERB, {kLEX, kSUBJ, kOBJ, kPREP_P, kADVERB, kADJ}},
    {kSUBJ, {kLEX, kDET, kADJ, kPREP_P}},
    {kOBJ, {kLEX, kDET, kADJ, kPREP_P}},
    {kPREP_P, {kLEX, kPREP, kADJ, kDET}},
    {kPREP, {kLEX}},
    {kADJ, {kLEX}},
    {kADVERB, {kLEX}},
    {kDET, {kLEX}},
    {kLEX, {}}};

/*******************************************
    下面实现的读出规则为 FIXED_MAP_READOUT。
    @todo 实现其它的读出规则。详见 `parser.py`。
    需要重新组织代码结构，允许我们自由选择读出规则。
*********************************************/
void ReadOut(BioParser::Parser &parser_brain,
             const std::string area_name,
             const std::map<std::string, std::vector<std::string>> readout_map,
             std::vector<std::vector<std::string>> &dependency)
{
    std::vector<std::string> to_areas = readout_map.at(area_name);
    std::map<std::string, std::vector<std::string>> proj_map;
    std::string this_word, other_word;

    for (std::string area : to_areas)
        proj_map[area] = {area_name};

    parser_brain.ProjectStar(proj_map);
    this_word = parser_brain.GetWord(kLEX, 0.7);

    for (std::string to_area : to_areas)
    {
        if (to_area == kLEX)
            continue;
        parser_brain.ProjectStar({{kLEX, {to_area}}});
        other_word = parser_brain.GetWord(kLEX, 0.7);
        dependency.push_back({this_word, other_word, to_area});
    }

    for (std::string to_area : to_areas)
    {
        if (to_area != kLEX)
            ReadOut(parser_brain, to_area, readout_map, dependency);
    }
}

/***********************************
 *          解析过程
 *  @todo 重新组织代码结构，使得解析过程能够重置，
 *  即一个解析器可以解析多条句子，并允许文件输入。
 * ********************************/
void Parse(std::string sentence,
           float p, int LEX_k = 20, int proj_rounds = 20,
           int non_LEX_n = 10000, int non_LEX_k = 100)
{
    using namespace BioParser;

    Parser parser_brain = Parser(p, kLEXEME_DICT, kAREAS, {kLEX, kSUBJ, kVERB}, kRECURRENT_AREAS);
    int LEX_n = LEX_k * kLEX_SIZE;
    std::stringstream ss(sentence);
    std::string word;

    parser_brain.AddExplicitArea(kLEX, LEX_n, LEX_k, kDEFAULT_BETA);

    parser_brain.AddArea(kSUBJ, non_LEX_n, non_LEX_k, kDEFAULT_BETA);
    parser_brain.AddArea(kVERB, non_LEX_n, non_LEX_k, kDEFAULT_BETA);
    parser_brain.AddArea(kOBJ, non_LEX_n, non_LEX_k, kDEFAULT_BETA);
    parser_brain.AddArea(kADJ, non_LEX_n, non_LEX_k, kDEFAULT_BETA);
    parser_brain.AddArea(kADVERB, non_LEX_n, non_LEX_k, kDEFAULT_BETA);
    parser_brain.AddArea(kPREP, non_LEX_n, non_LEX_k, kDEFAULT_BETA);
    parser_brain.AddArea(kPREP_P, non_LEX_n, non_LEX_k, kDEFAULT_BETA);

    parser_brain.AddArea(kDET, non_LEX_n, LEX_k, kDEFAULT_BETA);

    parser_brain.Initiate();

    parser_brain.UpdatePlasticities(kRECURRENT_AREAS, kLEX, kRECURRENT_BETA, kINTERAREA_BETA, kLEX_BETA);

    while (ss >> word)
    {
        GenericRules lexeme = kLEXEME_DICT.at(word);
        parser_brain.ActivateWord(kLEX, word);

        std::cout << "Activated word: " << word << std::endl;

        parser_brain.ApplyRules(lexeme.pre_rules);

        std::map<std::string, std::vector<std::string>> proj_map = parser_brain.GetProjectMap();
        for (auto ele : proj_map)
        {
            if (std::find(ele.second.begin(), ele.second.end(), kLEX) == ele.second.end())
            {
                parser_brain.FixAssembly(ele.first);
                std::cout << "Fixed assembly because no projection from LEX into " << ele.first << std::endl;
                continue;
            }
            else
            {
                if (ele.first == kLEX)
                    continue;
                for (auto e : proj_map)
                {
                    if (std::find(e.second.begin(), e.second.end(), ele.first) != e.second.end())
                    {
                        parser_brain.UnfixAssembly(ele.first);
                        parser_brain.getAreaByName(ele.first).winners_[0] = -1;
                        std::cout << "Erased assembly because of projection from LEX into " << ele.first << std::endl;
                        break;
                    }
                }
            }
        }
        proj_map = parser_brain.GetProjectMap();

        for (size_t i = 0; i < proj_rounds; i++)
        {
            parser_brain.Step();
        }

        parser_brain.ApplyRules(lexeme.post_rules);
    }

    parser_brain.UnfixAllAreas();
    parser_brain.DisablePlasticity();

    std::vector<std::vector<std::string>> dependencies = {};
    ReadOut(parser_brain, kVERB, kENGLISH_READOUT_RULES, dependencies);

    for (auto dependency : dependencies)
    {
        // Komari::Print(dependency);
        for (auto s : dependency)
        {
            std::cout << s << ' ';
        }
        std::cout << "| ";
    }
    std::cout << std::endl;

    std::cout << "Finished parsing!!!" << std::endl;
}

int main()
{
    Parse("dogs of the man saw a big cats in a woman", 0.1);
    return 0;
}